iT邦幫忙

2025 iThome 鐵人賽

DAY 1
0
Software Development

Laravel Pest TDD 實戰:從零開始的測試驅動開發系列 第 1

Day 01 - 環境設置與第一個測試 🚀

  • 分享至 

  • xImage
  •  

今天要做什麼?

歡迎來到「Laravel Pest TDD 實戰:從零開始的測試驅動開發」系列!

想像一下,你是一位新手開發者,剛加入一個重視程式品質的團隊。主管交給你第一個任務:「我們要建立一個 Laravel 專案,而且要從第一天就導入測試文化。」聽起來很有挑戰性?別擔心,讓我們從最基礎的開始,一步步建立起 TDD 的基礎。

今天是我們 TDD 旅程的第一天,我們要設置 Laravel + Pest 的開發環境,並寫下第一個測試。就像學開車一樣,我們先在安全的練習場地熟悉基本操作。

學習目標

今天結束後,你將學會:

  • 建立 Laravel + Pest 開發環境
  • 了解 Pest 的基本配置
  • 寫出第一個單元測試
  • 體驗測試失敗到通過的完整過程
  • 理解測試檔案的組織結構

TDD 學習地圖

第一階段:打好基礎(Day 1-10)
├── Day 01 - 環境設置與第一個測試 ★ 今天在這裡
├── ...
└── (更多精彩內容待續)

建立專案 🏗️

首先建立一個全新的 Laravel 專案:

composer create-project laravel/laravel laravel-tdd-demo
cd laravel-tdd-demo

安裝 Pest

Pest 是 Laravel 生態系統中現代化的測試框架,提供更簡潔的語法:

composer require pestphp/pest --dev --with-all-dependencies

初始化 Pest:

./vendor/bin/pest --init

配置 Pest

查看 tests/Pest.php 檔案,這是 Pest 的全域設定:

<?php

use Illuminate\Foundation\Testing\RefreshDatabase;

uses(Tests\TestCase::class)->in('Feature');

uses(
    Tests\TestCase::class,
    RefreshDatabase::class,
)->in('Feature');

我們可以為單元測試新增設定,更新 tests/Pest.php

<?php

use Illuminate\Foundation\Testing\RefreshDatabase;

uses(Tests\TestCase::class)->in('Feature');

uses(
    Tests\TestCase::class,
    RefreshDatabase::class,
)->in('Feature');

// 單元測試設定
uses()->in('Unit');

專案結構

Laravel 的測試檔案預設分為兩種:

  • tests/Unit/: 單元測試,測試單一類別或函數
  • tests/Feature/: 功能測試,測試完整的功能流程

今天我們專注於單元測試。

第一個測試:計算機類別 🧮

讓我們從最簡單的例子開始 - 一個計算機類別。

建立 tests/Unit/Day01/CalculatorTest.php

<?php

// 先寫測試,這時 Calculator 類別還不存在
use App\Calculator;

it('adds two numbers correctly', function () {
    $calculator = new Calculator();
    $result = $calculator->add(2, 3);
    expect($result)->toBe(5);
});

執行測試(紅燈階段)🔴

./vendor/bin/pest

你會看到測試失敗的訊息,因為 Calculator 類別還不存在。這就是 TDD 的「紅燈」階段 - 先寫測試,看它失敗。

實作程式碼(綠燈階段)🟢

建立 app/Calculator.php

<?php

namespace App;

class Calculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }
}

再次執行測試:

./vendor/bin/pest

現在測試應該通過了!這就是「綠燈」階段。

新增更多測試案例

更新 tests/Unit/Day01/CalculatorTest.php

<?php

use App\Calculator;

describe('Calculator', function () {
    beforeEach(function () {
        $this->calculator = new Calculator();
    });

    it('adds two numbers correctly', function () {
        $result = $this->calculator->add(2, 3);
        expect($result)->toBe(5);
    });

    it('adds negative numbers correctly', function () {
        $result = $this->calculator->add(-2, 3);
        expect($result)->toBe(1);
    });

    it('subtracts two numbers correctly', function () {
        $result = $this->calculator->subtract(5, 3);
        expect($result)->toBe(2);
    });

    it('multiplies two numbers correctly', function () {
        $result = $this->calculator->multiply(4, 3);
        expect($result)->toBe(12);
    });

    it('divides two numbers correctly', function () {
        $result = $this->calculator->divide(10, 2);
        expect($result)->toBe(5);
    });

    it('handles division by zero', function () {
        expect(fn() => $this->calculator->divide(10, 0))
            ->toThrow(DivisionByZeroError::class);
    });
});

完整實作 Calculator 類別

更新 app/Calculator.php

<?php

namespace App;

class Calculator
{
    public function add(int $a, int $b): int
    {
        return $a + $b;
    }

    public function subtract(int $a, int $b): int
    {
        return $a - $b;
    }

    public function multiply(int $a, int $b): int
    {
        return $a * $b;
    }

    public function divide(int $a, int $b): int
    {
        if ($b === 0) {
            throw new \DivisionByZeroError('Division by zero');
        }
        return intval($a / $b);
    }
}

執行測試確認

./vendor/bin/pest

所有測試都應該通過!

測試執行選項

Pest 提供多種執行測試的方式:

# 監聽模式
./vendor/bin/pest --watch

# 單次執行
./vendor/bin/pest

# 使用詳細輸出
./vendor/bin/pest --verbose

# 產生覆蓋率報告(需要安裝 Xdebug)
./vendor/bin/pest --coverage

# 平行執行測試
./vendor/bin/pest --parallel

常見問題解決 🔧

1. Class not found 錯誤

如果遇到「Class 'App\Calculator' not found」錯誤,確認:

  • 檔案路徑和命名空間正確
  • 執行 composer dump-autoload 重新載入自動載入

2. DivisionByZeroError not found

如果遇到 Class 'DivisionByZeroError' not found 錯誤,確保你的 PHP 版本 >= 7.0。

3. Pest 找不到測試檔案

確保測試檔案符合以下命名規則之一:

  • *Test.php
  • 位於 tests/ 目錄下

測試組織最佳實踐

每個測試都應該遵循 AAA 模式:

  1. Arrange(準備):設定測試資料和環境
  2. Act(執行):執行要測試的功能
  3. Assert(驗證):檢查結果是否符合預期

小挑戰 🎯

試著為計算機新增以下功能:

  1. 餘數運算:實作 modulo($a, $b) 方法
  2. 次方運算:實作 power($base, $exponent) 方法
  3. 錯誤處理:當輸入不是數字時該如何處理?

💡 提示:記得先寫測試再實作功能!

今天學到什麼?

1. 測試驅動開發的基本循環

  • 紅燈:先寫測試,看它失敗
  • 綠燈:寫最少的程式碼讓測試通過
  • 重構:改善程式碼品質

2. Pest 基礎概念

  • it(): 個別測試案例,使用自然語言描述
  • describe(): 測試群組,組織相關測試
  • expect(): 斷言函數,驗證結果
  • beforeEach(): 在每個測試前執行

3. 測試檔案組織

  • 單元測試放在 tests/Unit/ 目錄
  • 按天數組織學習內容(如 tests/Unit/Day01/CalculatorTest.php
  • 使用 Test.php 後綴命名
  • 測試檔案結構應該反映被測試的程式碼結構

4. PHP 類型提示的價值

  • 編譯時期就能發現型別錯誤
  • 程式碼更容易理解和維護
  • IDE 提供更好的自動完成

今天的收穫

今天我們完成了 TDD 學習旅程的第一步!透過建立 Laravel + Pest 環境,我們學會了:

  • 如何設置現代化的測試環境
  • TDD 的基本循環:紅燈 → 綠燈 → 重構
  • Pest 簡潔優雅的測試語法
  • PHP 類型提示在測試中的重要性

總結

今天我們成功建立了 Laravel + Pest 的測試環境,並完成了第一個單元測試。雖然例子很簡單,但我們已經體驗了完整的 TDD 流程:

  1. 先寫測試(紅燈)
  2. 實作功能(綠燈)
  3. 確保測試通過

記住,TDD 不只是一種測試技術,更是一種設計思維。透過先寫測試,我們被迫思考程式的介面和行為,這能幫助我們寫出更好的程式碼。

明天我們將深入了解斷言函數的威力,學習如何寫出更有表達力的測試。準備好了嗎?繼續加油! 💪


下一篇
Day 02 - 認識斷言(Assertions) 🚀
系列文
Laravel Pest TDD 實戰:從零開始的測試驅動開發9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言